using System;
using System.Linq;
using System.Data;
using System.Text;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.SQLDom;
using DataColumn = T.FAS.Runtime.Metadata.Entity.DataModel.DataColumn;
using T.FAS.Runtime.DataAccess.Engine.ValidateChecker.Entity;

namespace T.FAS.Runtime.DataAccess.Engine
{
    public class DataContextUtil
    {
        #region Fields & Property

        private DateTime fasMinDate = System.Convert.ToDateTime("1900-01-01 00:00:00");
        /// <summary>
        /// CDO外部输入校验结果集合
        /// </summary>
        public List<ItemCheckResult> CDOValidateCheckResult = new List<ItemCheckResult>();
        private DataAccessOpType dataAccessOpType = DataAccessOpType.I;
        /// <summary>
        /// update类型操作的返回结果
        /// </summary>
        private UpdateDataContextSourceData updateDataContextTables = new UpdateDataContextSourceData();
        private Dictionary<string, object> _controlContext = new Dictionary<string, object>();
        #endregion

        #region Insert

        /// <summary>
        /// 将ListTRecord转换为List<DataTable>集合，仅限Insert操作
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="record"></param>
        /// <param name="controlContext"></param>
        /// <returns></returns>
        public List<DataTable> ConvertToDataTableForInsert(CompositeDataObject cdo, ListTRecord record, SqlExecuteControlContext controlContext = null)
        {
            InitContext(cdo);
            this.dataAccessOpType = DataAccessOpType.I;
            List<DataTable> dt = Convert(cdo.RootCompositeNodeObject, record.DataResult, controlContext);
            ValidateHandler(cdo, CDOValidateCheckResult);
            return dt;
        }

        /// <summary>
        /// 将TRecord转换为List<DataTable>集合，仅限Insert操作
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="record"></param>
        /// <param name="controlContext"></param>
        /// <returns></returns>
        public List<DataTable> ConvertToDataTableForInsert(CompositeDataObject cdo, TRecord record, SqlExecuteControlContext controlContext = null)
        {
            InitContext(cdo);
            this.dataAccessOpType = DataAccessOpType.I;
            dynamic nodeRecord = (TRecord)(record[cdo.RootCompositeNodeObject.DataObjectTableAlias]); //TRecord顶点包了一层需要先解开
            if (nodeRecord == null)
                nodeRecord = record;
            List<DataTable> dt = Convert(cdo.RootCompositeNodeObject, new List<TRecord> { nodeRecord }, controlContext);
            ValidateHandler(cdo, CDOValidateCheckResult);
            return dt;
        }

        /// <summary>
        /// 将ListRecord转换为内部List<DataTable>集合
        /// 1:Record中的列都为ele.Alias ?? ele.Name,
        /// 2:DataTable中的Column.ColumnName都为DataObject.Columns[].ColumnName
        /// </summary>
        /// <param name="cno">模型节点</param>
        /// <param name="records">records集合对应同一CNO节点的多条记录</param>
        /// <returns></returns>
        private List<DataTable> Convert(CompositeNodeObject cno, List<TRecord> records, SqlExecuteControlContext controlContext = null)
        {
            if (records.Count == 0)
                return default;
            List<DataTable> dataTables = new List<DataTable>();
            Dictionary<string, string> dict = new Dictionary<string, string>();
            DataTable current = new DataTable
            {
                TableName = cno.ID
            };
            //初始化DataTable,以CNO节点元素对应的普通列作为Column
            /*
             * 低代码改动:
             * 问题：根据Normal Element构造列的模式只支持全量更新
             * 
             */
            FillTableColumn(current, cno, controlContext);
            //获取当前CNO节点的主/外键元素Element
            //var FKElement = cno.GetInnerAssociateElement();
            var pkElement = cno.GetPKElement();

            foreach (TRecord record in records)
            {
                if (record == null) continue;
                //根据Record构造新行
                DataRow dataRow = current.NewRow();
                foreach (var column in cno.DataObject.Columns)
                {
                    var ele = cno.Elements.Find(e => e.DataColumnID == column.ID);
                    if (ele == null)
                    {
                        throw new FasException(DataAccessErrorCode.CompositeDataObject.ElementError, string.Format(CommonStringClassEngine.DataContextUtil_CDONodeLackElementRelatedToDOColumn, cno.CompositeDataObjectID, cno.ID, cno.DataObject.Code, column.ColumnName, cno.DataObject.Code));
                    }
                    if (column.IsSystemField()) continue;  //新增时跳过系统字段处理
                    bool checkFlag = column.IsFkColumn;
                    ////如果非主节点，
                    //if (!cno.IsRootObject)
                    //{
                    //    //当前字段为外键的情况
                    //    if(ele.ID == FKElement.ID)
                    //    {
                    //        //新增时将子节点
                    //        if (dataAccessOpType == DataAccessOpType.I)
                    //            record[column.ColumnName] = FKValue;
                    //    }
                    //}
                    //if(ele.ID == PKElement.ID)
                    //{
                    //    /*todo: 对接主键生成规则服务*/
                    //}

                    //如果TRecord中没有该属性，跳过。如果为国际化列，认为主语言列没传入时，辅助语言列一样跳过不处理
                    if (!record.IsContainProperty(column.ColumnName)) 
                    {
                        continue;
                    }

                    //校验数据权限，跳过没有权限的列
                    if (controlContext != null && !controlContext.IsHasPermission(cno.DataObject, column))
                    {
                        continue;
                    }

                    object inputVal = record[column.ColumnName];
                    //取Record列, 由于现在Ele.Name=DataColumn.ColumnName，暂时这么取值
                    if (column.IsTimestampField() || column.DataType == DataType.Timestamp.ToString()) //如果是时间戳字段
                    {
                        inputVal = ConvertTimestampData(inputVal);
                    }
                    object transformData = TransformInputData(cno, inputVal, ele);
                    dataRow[column.ColumnName] = transformData;
                    ItemCheckResult checkResult = CDOValidateChecker.Check(cno, ele, inputVal, checkFlag, _controlContext);
                    CDOValidateCheckResult.Add(checkResult);
                    //数据国际化改造
                    if (column.IsInternational)
                    {
                        //数据国际化改造：CDO.Create支持多语言列插入
                        //先根据上下文中的语言区域设置获取当前要处理的语言区域列表
                        //在DataRow中构造所有启用国际化的列&辅助列（辅助列可能在TRecord中不存在，需要构造为null)
                        Dictionary<string, string> mapping = InternationalTools.GetLanguageMapping(controlContext.InternationalMode, controlContext.SpecifiedLanguages);
                        if (mapping.Count > 0)
                        {
                            var i18NControlContext = GetNewContext();
                            i18NControlContext[DACConst.CDO.IsI18NVal] = true;
                            foreach (string langKey in mapping.Keys)
                            {
                                i18NControlContext[DACConst.I18NCode] = mapping[langKey];
                                string i18NFieldName = InternationalTools.GetLogicFieldName(column.ColumnName, mapping[langKey]); //eg:Name_en_US
                                object i18NInputVal = record[i18NFieldName];
                                string fieldName = InternationalTools.GetRealFieldName(cno.DataObject, column.ColumnName, langKey); //eg:Name_lang1
                                object i18NTransformData = TransformInputData(cno, i18NInputVal, ele, true);
                                dataRow[fieldName] = i18NTransformData;  //eg:Name_lang1: testVal
                                ItemCheckResult i18NCheckResult = CDOValidateChecker.Check(cno, ele, i18NInputVal, checkFlag, i18NControlContext);
                                CDOValidateCheckResult.Add(checkResult);
                            }
                        }
                    }
                    //统一数据访问服务权限&掩码改造
                    if (MaskTool.EnableFASMaskStorage(column.MaskingRule))
                    {
                        //获取掩码物理字段名
                        string maskFieldName = DataColumn.GetMaskColumnName(cno.DataObject, column.ColumnName);
                        //获取原始输入值的字符串值
                        string originDataStr = transformData?.ToString();
                        //获取掩码值
                        dataRow[maskFieldName] = MaskTool.CalculateMaskData(column.MaskingRule, originDataStr);
                        //掩码值不需要校验
                    }
                }
                current.Rows.Add(dataRow);
            }
            dataTables.Add(current);
            if (cno.ChildCompositeNodeObjects != null)
            {
                foreach (var child in cno.ChildCompositeNodeObjects)
                {
                    //获取子节点的主/外键元素Element
                    var childFKElement = child.GetInnerAssociateElement();
                    var childPKElement = child.GetPKElement();

                    List<TRecord> childRecords = new List<TRecord>();
                    //处理子节点的Record数据，将子节点的外键属性赋为主节点主键
                    foreach (var record in records)
                    {
                        var isList = child.GetRelationType(cno) == 0;
                        var nodeAlias = TUtil.GetPropertyName(child.DataObjectTableAlias, isList);
                        List<TRecord> cRecords = GetChildNodesRecords(record, nodeAlias, isList);
                        if (cRecords != null)
                        {
                            //将子节点的外键属性赋为主节点主键
                            foreach (var cRecord in cRecords)
                            {
                                //新增记录时，将子节点的外键赋值为主节点的主键ID
                                if (dataAccessOpType == DataAccessOpType.I)
                                    cRecord[childFKElement.Alias ?? childFKElement.Name] = record[pkElement.Alias ?? pkElement.Name];
                                //todo:将子节点的主键id对接主键生成规则
                                //cRecord[childPKElement.Alias ?? childPKElement.Name] = 
                            }
                            childRecords.AddRange(cRecords);
                        }
                    }
                    if (childRecords.Count > 0)
                    {
                        var result = Convert(child, childRecords);
                        if (result != null)
                            dataTables.AddRange(result);
                    }
                }
            }
            return dataTables;
        }

        #endregion

        #region Update
        /// <summary>
        /// 从CDO的Root节点开始递归遍历
        /// 将ListTRecord转换为List<DataTable>集合，仅限Insert操作
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="record"></param>
        /// <param name="dataAccessOpType"></param>
        /// <returns></returns>
        public UpdateDataContextSourceData ConvertForUpdate(CompositeDataObject cdo, ListTRecord record, SqlExecuteControlContext controlContext = null)
        {
            InitContext(cdo);
            this.dataAccessOpType = DataAccessOpType.U;
            ConvertForUpdate(cdo.RootCompositeNodeObject, record.DataResult, controlContext);
            ValidateHandler(cdo, CDOValidateCheckResult);
            return updateDataContextTables;
        }

        /// <summary>
        /// 从CDO的Root节点开始递归遍历
        /// 将TRecord转换为List<DataTable>集合，仅限Update操作
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        public UpdateDataContextSourceData ConvertForUpdate(CompositeDataObject cdo, TRecord record, SqlExecuteControlContext controlContext = null)
        {
            InitContext(cdo);
            this.dataAccessOpType = DataAccessOpType.U;
            dynamic nodeRecord = (TRecord)(record[cdo.RootCompositeNodeObject.DataObjectTableAlias]); //TRecord顶点包了一层需要先解开
            if (nodeRecord == null)
                nodeRecord = record;
            ConvertForUpdate(cdo.RootCompositeNodeObject, new List<TRecord> { nodeRecord }, controlContext);
            ValidateHandler(cdo, CDOValidateCheckResult);
            return updateDataContextTables;
        }

        /// <summary>
        /// 将TRecord类型的数据转为统一数据访问服务标准DataTable格式
        /// Logic:
        /// 1、records类型为
        /// </summary>
        /// <param name="cno">模型节点</param>
        /// <param name="records">records集合</param>
        /// <returns></returns>
        private void ConvertForUpdate(CompositeNodeObject cno, List<TRecord> records, SqlExecuteControlContext controlContext = null, int depth = 0)
        {
            #region 校验
            if (cno == null || records == null) return;
            //if (cno.IsRootObject)
            //{
            //    //顶级节点过滤所有变更类型为Update的记录
            //    records = records.Where(r => r.ChangeType == TRecordChangeType.Update).ToList();
            //}
            //if (records.Count == 0) return;
            #endregion
            try
            {
                var pkElement = cno.GetPKElement(); //获取当前CNO节点的主/外键元素Element
                var addRecords = records.Where(r => r.ChangeType == TRecordChangeType.Add || (r.ChangeSet != null && r.ChangeSet.ChangeType == TRecordChangeType.Add)).ToList();
                var delRecords = records.Where(r => r.ChangeType == TRecordChangeType.Delete || (r.ChangeSet != null && r.ChangeSet.ChangeType == TRecordChangeType.Delete)).ToList();
                var updateRecords = records.Where(r => r.ChangeType == TRecordChangeType.Update || (r.ChangeSet != null && r.ChangeSet.ChangeType == TRecordChangeType.Update)).ToList();
                var needRecursiveRecords = new List<TRecord>(); //需要递归处理子级的Record
                                                                //needRecursiveRecords.AddRange(addRecords);
                                                                //needRecursiveRecords.AddRange(updateRecords);
                                                                //应对一种场景：子TRecord的ChangeType=Unchanged时，状态机保存前事件对TRecord进行.赋值操作时，不会冒泡修改主TRecord的ChangeType。所以Unchanged类型的TRecord依然要递归
                needRecursiveRecords.AddRange(records.Where(r => r.ChangeType != TRecordChangeType.Delete));

                List<object> delDatas = default;
                DataTable insertData = new DataTable     //当前节点新增的TRecord映射到DataTable中
                {
                    TableName = cno.ID
                };
                var updateData = new List<UpdateChangeSet>();

                //初始化DataTable,以CNO节点元素对应的普通列作为Column
                FillTableColumn(insertData, cno, controlContext);

                #region 处理当前节点的TRecord
                //处理新增数据
                if (addRecords != null && addRecords.Count > 0)
                {
                    foreach (var record in addRecords)
                    {
                        FillUpdateNewRow(cno, record, insertData, controlContext);
                    }
                }
                //处理修改数据
                if (updateRecords != null && updateRecords.Count > 0)
                {
                    foreach (var record in updateRecords)
                    {
                        if (record?.ChangeSet?.ChangeSetItems == null)
                        {
                            throw new FasException(DataAccessErrorCode.Update.Error, string.Format(CommonStringClassEngine.DataContextUtil_ConvertForUpdate,cno.CompositeDataObject?.Code,record.GetPKValue(cno)));
                        }
                        //生成DAC内部的UpdateChangeSet，防止处理ChangeSetItems时影响后续状态机处理
                        UpdateChangeSet changeSet = record.GetUpdataChangeSetForEngine(cno);
                        ProcessChangeSetItem(cno, changeSet, controlContext);
                        updateData.Add(changeSet);
                    }
                }
                //处理删除数据
                delDatas = delRecords.Select(r => r[pkElement.Alias ?? pkElement.Name]).ToList();   //待删除record主键列集合
                #endregion

                #region 递归处理子节点数据，只需要处理ChangeType为Add | Update的Record的子级
                if (cno.ChildCompositeNodeObjects != null)
                {
                    foreach (var child in cno.ChildCompositeNodeObjects)
                    {
                        //获取子节点的主/外键元素Element
                        var childFKElement = child.GetInnerAssociateElement();
                        var childPKElement = child.GetPKElement();
                        //递归处理的子集结合
                        List<TRecord> childRecords = new List<TRecord>();
                        //处理子节点的Record数据，将子节点的外键属性赋为主节点主键

                        foreach (var record in needRecursiveRecords)
                        {
                            var isList = child.GetRelationType(cno) == 0;
                            var nodeAlias = TUtil.GetPropertyName(child.DataObjectTableAlias, isList);
                            List<TRecord> childs = GetChildNodesRecords(record, nodeAlias, isList);
                            if (childs != null)
                            {
                                //将子节点的外键属性赋为主节点主键
                                foreach (var cRecord in childs)
                                {
                                    //todo:将子节点的主键id对接主键生成规则，现在需要前端生成或状态机赋值
                                    //新增记录时，将子节点的外键赋值为主节点的主键ID
                                    if (cRecord.ChangeType == TRecordChangeType.Add || (cRecord.ChangeSet != null && cRecord.ChangeSet.ChangeType == TRecordChangeType.Add))
                                        cRecord[childFKElement.Alias ?? childFKElement.Name] = record[pkElement.Alias ?? pkElement.Name];
                                    //更新记录时，应该忽略掉关联父级节点的外键变更
                                    if (cRecord.ChangeType == TRecordChangeType.Update)
                                    {
                                        if (cRecord.ChangeSet?.ChangeSetItems?.Count > 0)
                                        {
                                            var parentForeignColumn = cRecord.ChangeSet.ChangeSetItems.FirstOrDefault(item => item.ColumnCode == (childFKElement.Alias ?? childFKElement.Name));
                                            if (parentForeignColumn != null)
                                            {
                                                cRecord.ChangeSet.ChangeSetItems.Remove(parentForeignColumn);
                                            }
                                        }
                                    }
                                }
                                childRecords.AddRange(childs);
                            }
                        }
                        if (childRecords.Count > 0)
                            ConvertForUpdate(child, childRecords);
                    }
                }
                #endregion

                if (updateData.Count > 0)
                    updateDataContextTables.UpdateData[cno.ID] = updateData;
                if (delDatas.Count > 0)
                    updateDataContextTables.DeleteData[cno.ID] = delDatas;
                //addRecords不为空时才需要插入
                if (addRecords.Count > 0 && insertData.Rows.Count > 0)
                    updateDataContextTables.InsertData.Add(insertData);
            }
            catch (Exception e)
            {
                throw new Exception(string.Format(CommonStringClassEngine.DataContextUtil_ConvertForUpdate1,e));
            }
        }
        #endregion

        #region Funcs

        /// <summary>
        /// CDO输入校验器
        /// </summary>
        /// <param name="cdo"></param>
        /// <param name="results"></param>
        private void ValidateHandler(CompositeDataObject cdo, List<ItemCheckResult> results)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in results)
            {
                if (item.Flag == false)
                    sb.Append(item.Msg);
            }
            if (results.Any(o => o.Flag == false))
                throw new FasException(DataAccessErrorCode.ValidInput.ValidInputError, string.Format(CommonStringClassEngine.DataContextUtil_ValidateHandler,cdo.Code,sb));
        }

        /// <summary>
        /// 根据cno中的element，填充dt的列信息，跳过系统字段，***注意数据类型，尤其是时间
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="cno"></param>
        /// <param name="controlContext"></param>
        private void FillTableColumn(DataTable dt, CompositeNodeObject cno, SqlExecuteControlContext controlContext = null)
        {
            foreach (var ele in cno.Elements.Where(ele => ele.ElementType == ElementType.Normal))
            {
                DataColumn col = ele.GetDataColumn(cno);
                if (col.IsSystemField())
                {
                    continue;
                }
                //校验数据权限，跳过没有权限的列
                if (controlContext != null && !controlContext.IsHasPermission(cno.DataObject, col))
                {
                    continue;
                }
                System.Data.DataColumn newColumn = dt.Columns.Add(col.ColumnName);
                newColumn.DataType = MappingApplicationTypeToCSharpType(ele.DataType);
                newColumn.AllowDBNull = ele.IsAllowNull;

                if (col.IsInternational)
                {
                    InternationalMode mode = InternationalMode.All;
                    List<string> specifiedLanguages = null;
                    if (controlContext != null)
                    {
                        mode = controlContext.InternationalMode;
                        specifiedLanguages = controlContext.SpecifiedLanguages;
                    }
                    Dictionary<string, string> mapping = InternationalTools.GetLanguageMapping(mode, specifiedLanguages); 
                    if (mapping.Count > 0)
                    { 
                        foreach (string langKey in mapping.Keys)
                        {
                            string realTableFieldName = InternationalTools.GetRealFieldName(cno.DataObject, col.ColumnName, langKey);
                            System.Data.DataColumn i18NColumn = dt.Columns.Add(realTableFieldName);
                            i18NColumn.DataType = newColumn.DataType;
                            i18NColumn.AllowDBNull = true;
                        }
                    }
                }
                //统一数据访问服务掩码改造
                if (MaskTool.EnableFASMaskStorage(col.MaskingRule))
                {
                    string realTableFieldName = DataColumn.GetMaskColumnName(cno.DataObject, col.ColumnName);
                    System.Data.DataColumn maskColumn = dt.Columns.Add(realTableFieldName);
                    maskColumn.DataType = typeof(string);
                    maskColumn.AllowDBNull = true;
                }
            }
        }

        /// <summary>
        /// 根据cno中的element，填充dt的列信息，***注意数据类型，尤其是时间
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="cno"></param>
        /// <returns></returns>
        private void FillDictColumn(Dictionary<string, object> dt, CompositeNodeObject cno)
        {
            foreach (var ele in cno.Elements.Where(ele => ele.ElementType == ElementType.Normal))
            {
                var col = ele.GetDataColumn(cno);
                dt.Add(col.ColumnName, default);
            }
        }

        /// <summary>
        /// 将FAS应用程序类型映射为C#类型，For DataTable AddColumn使用
        /// </summary>
        /// <returns></returns>
        private Type MappingApplicationTypeToCSharpType(DataType dataType)
        {
            var typer = DataTypeToolFactory.GetTypeMapping(dataType);
            if (typer == null)
                throw new FasException(DataAccessErrorCode.ValidInput.NotSupportedType, string.Format(CommonStringClassEngine.NotSupportDataType,dataType));
            else
                return typer.GetCSharpType();
        }

        /// <summary>
        /// 输入值校验及转化
        /// </summary>
        /// <param name="cno"></param>
        /// <param name="inputObj"></param>
        /// <param name="ele"></param>
        /// <param name="isI18NValue"></param>
        /// <returns></returns>
        private object TransformInputData(CompositeNodeObject cno, object inputObj, CompositeNodeObjectElement ele, bool isI18NValue = false)
        {
            string errirPrefix = $"CDO:{cno?.CompositeDataObject?.Code},CNO:{cno?.DataObjectTableAlias}";
            if (ele == null || ele.DataColumn == null)
                throw new FasException(DataAccessErrorCode.ValidInput.ValidInputError, string.Format(CommonStringClassEngine.DataContextUtil_TransformInputData,errirPrefix));
            DataType dataType = ele.DataType;
            bool isAllowNull = ele.IsAllowNull;
            if (ele.DataColumn.IsInternational && isI18NValue == true)
            {
                isAllowNull = true;
            }
            //掩码主列的值走正常校验，掩码值不走转换校验
            //else if (ele.DataColumn.MaskingRule.IsEnable)
            //{
                //isAllowNull = true;
            //}
            var typer = DataTypeToolFactory.ValidAndConvertToObject(dataType);
            if (typer == null)
                throw new FasException(DataAccessErrorCode.ValidInput.NotSupportedType, string.Format(CommonStringClassEngine.NotSupportDataType, dataType));
            else
            {
                try
                {
                    return typer.ValidAndConvertToObject(inputObj, isAllowNull);
                }
                catch (Exception e)
                {
                    throw new FasException(DataAccessErrorCode.ValidInput.ValidInputError, string.Format(CommonStringClassEngine.DataContextUtil_TransformInputData2,errirPrefix,ele.Name,e.Message));
                }
            }
        }

        /// <summary>
        /// 获取下级节点TRecords集合
        /// </summary>
        /// <param name="record"></param>
        /// <param name="childAlias"></param>
        /// <param name="isList"></param>
        /// <returns></returns>
        private List<TRecord> GetChildNodesRecords(TRecord currRecord, string childAlias, bool isList)
        {
            object childObj = currRecord[childAlias];
            var result = new List<TRecord>();
            if (childObj == null)
                return result;
            if (isList)
            {
                if (childObj is List<TRecord> listRecord)
                    return listRecord;
            }
            else
            {
                if (childObj is TRecord record)
                {
                    if (record.Count == 0)
                        return result;
                    return new List<TRecord> { record };
                }
            }
            throw new FasException(DataAccessErrorCode.CompositeDataObject.Error, string.Format(CommonStringClassEngine.DataContextUtil_GetChildNodesRecords2,childAlias,childObj.GetType(),(isList ? CommonStringClassEngine.DataContextUtil_GetChildNodesRecords: CommonStringClassEngine.DataContextUtil_GetChildNodesRecords1)));
        }
        #endregion

        #region Private Funcs
        /// <summary>
        /// 将TRecord映射为DataRow添加到DataTable中
        /// </summary>
        /// <param name="cno"></param>
        /// <param name="record"></param>
        /// <param name="InsertData"></param>
        private void FillUpdateNewRow(CompositeNodeObject cno, TRecord record, DataTable InsertData, SqlExecuteControlContext controlContext = null)
        {
            //根据Record构造新行
            DataRow dataRow = InsertData.NewRow();
            foreach (var column in cno.DataObject.Columns)
            {
                if (column.IsSystemField())
                    continue;
                //校验数据权限，跳过没有权限的列
                if (controlContext != null && !controlContext.IsHasPermission(cno.DataObject, column))
                {
                    continue;
                }
                var checkFlag = column.IsFkColumn;
                var ele = cno.Elements.Find(e => e.DataColumnID == column.ID);
                if (ele == null)
                {
                    throw new FasException(DataAccessErrorCode.CompositeDataObject.ElementError, string.Format(CommonStringClassEngine.DataContextUtil_CDONodeLackElementRelatedToDOColumn, cno.CompositeDataObject?.Code ?? cno.CompositeDataObjectID, cno.DataObjectTableAlias, cno.DataObject.Code, column.ColumnName, cno.DataObject.Code));
                }

                #region todo
                //if(ele.ID == PKElement.ID)
                //{
                //    /*todo: 对接主键生成规则服务*/
                //}
                #endregion

                object inputVal = record[column.ColumnName];
                //非国际化字段处理
                if (!column.IsInternational && !column.MaskingRule.IsEnable)
                {
                    //特殊处理时间戳字段
                    if (column.IsTimestampField() || column.DataType == DataType.Timestamp.ToString())
                    {
                        inputVal = ConvertTimestampData(inputVal);
                    }
                    //将外部输入的值转换为C#类型
                    var transformData = TransformInputData(cno, inputVal, ele);
                    dataRow[column.ColumnName] = transformData;
                    //全局校验输入参数是否合规
                    ItemCheckResult checkResult = CDOValidateChecker.Check(cno, ele, inputVal, checkFlag, _controlContext);
                    CDOValidateCheckResult.Add(checkResult);
                }
                //掩码字段处理
                if (MaskTool.EnableFASMaskStorage(ele.DataColumn.MaskingRule))
                {
                    //将外部输入的值转换为C#类型
                    object transformData = TransformInputData(cno, inputVal, ele);
                    //获取掩码物理字段名
                    string maskFieldName = DataColumn.GetMaskColumnName(cno.DataObject, column.ColumnName);
                    //获取原始输入值的字符串值
                    string originDataStr = transformData?.ToString();
                    //获取掩码值
                    dataRow[maskFieldName] = MaskTool.CalculateMaskData(column.MaskingRule, originDataStr);
                    //掩码值不需要校验
                }
                //国际化字段
                if(column.IsInternational)
                {
                    //将外部输入的值转换为C#类型
                    object transformData = TransformInputData(cno, inputVal, ele);
                    dataRow[column.ColumnName] = transformData;
                    //全局校验输入参数是否合规
                    ItemCheckResult checkResult = CDOValidateChecker.Check(cno, ele, inputVal, checkFlag, _controlContext);
                    CDOValidateCheckResult.Add(checkResult);

                    Dictionary<string, string> mapping;
                    if (controlContext != null)
                    {
                        mapping = InternationalTools.GetLanguageMapping(controlContext.InternationalMode, controlContext.SpecifiedLanguages);
                    }
                    else
                    {
                        mapping = InternationalTools.GetLanguageMapping();
                    }
                    if (mapping.Count > 0)
                    {
                        var i18NControlContext = GetNewContext();
                        i18NControlContext[DACConst.CDO.IsI18NVal] = true;
                        foreach (var langKey in mapping.Keys)
                        {
                            i18NControlContext[DACConst.I18NCode] = mapping[langKey];
                            string i18NFieldName = InternationalTools.GetLogicFieldName(column.ColumnName, mapping[langKey]); //eg:Name_en_US
                            object i18NInputVal = record[i18NFieldName];
                            string fieldName = InternationalTools.GetRealFieldName(cno.DataObject, column.ColumnName, langKey); //eg:Name_lang1
                            object i18NTransformData = TransformInputData(cno, i18NInputVal, ele, true);
                            dataRow[fieldName] = transformData;  //eg:Name_lang1: testVal
                            ItemCheckResult i18NCheckResult = CDOValidateChecker.Check(cno, ele, inputVal, checkFlag, i18NControlContext);
                            CDOValidateCheckResult.Add(checkResult);
                        }
                    }
                }
                
            }
            InsertData.Rows.Add(dataRow);
        }

        /// <summary>
        /// 处理TChangeSet中的变更项，将其转换为C#类型，注意是否会对后续状态机执行产生影响
        /// </summary>
        /// <param name="cno"></param>
        /// <param name="changeSet"></param>
        /// <param name="controlContext"></param>
        private void ProcessChangeSetItem(CompositeNodeObject cno, UpdateChangeSet changeSet, SqlExecuteControlContext controlContext = null)
        {
            List<TChangeSetItem> maskItems = new List<TChangeSetItem>();
            List<TChangeSetItem> noAuthItems = new List<TChangeSetItem>();
            List<TChangeSetItem> removeItems = new List<TChangeSetItem>(); //当变更集中出现一些不存在的列时，剔除
            foreach (TChangeSetItem item in changeSet.ChangeSetItems)
            {
                Dictionary<string, object> _controlContext = GetNewContext();
                DataColumn column = cno.DataObject.Columns.FirstOrDefault(col => col.ColumnName == item.ColumnCode);
                //如果没匹配到字段名，则进行国际化字段判断
                if (column == null)
                {
                    InternationalMode mode = controlContext == null ? InternationalMode.All : controlContext.InternationalMode;
                    var mapping = InternationalTools.GetLanguageMapping(mode, controlContext?.SpecifiedLanguages);
                    if (mapping.Count > 0)
                    {
                        foreach (var i18NCode in mapping.Values)
                        {
                            var i18NSuffix = InternationalTools.GetLogicFieldNameSuffix(i18NCode);
                            if (item.ColumnCode.EndsWith(i18NSuffix))
                            {
                                string realColumnName = item.ColumnCode.Replace(i18NSuffix, "");
                                column = cno.DataObject.Columns.FirstOrDefault(col => col.ColumnName == realColumnName);
                                if (column != null)
                                {
                                    _controlContext[DACConst.CDO.IsI18NVal] = true;
                                    _controlContext[DACConst.I18NCode] = i18NCode;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (column == null) 
                {
                    removeItems.Add(item);
                    //跳过不存在于DO中的列
                    continue;
                }   
                bool checkFlag = column.IsFkColumn;
                CompositeNodeObjectElement ele = cno.Elements.Find(e => e.DataColumnID == column.ID);
                if (ele == null)
                {
                    throw new FasException(DataAccessErrorCode.CompositeDataObject.ElementError, string.Format(CommonStringClassEngine.DataContextUtil_CDONodeLackElementRelatedToDOColumn, cno.CompositeDataObject?.Code, cno.DataObjectTableAlias, cno.DataObject.Code, column.ColumnName, cno.DataObject.Code));
                }

                //校验数据权限，跳过没有权限的列
                if (controlContext != null && !controlContext.IsHasPermission(cno.DataObject, column))
                {
                    noAuthItems.Add(item);
                    continue;
                }

                object inputVal = item.NewValue;
                //特殊处理时间戳字段
                if (column.IsTimestampField() || column.DataType == DataType.Timestamp.ToString())
                {
                    inputVal = ConvertTimestampData(inputVal);
                }
                //将外部输入的值转换为C#类型
                object transformData = TransformInputData(cno, inputVal, ele);
                item.NewValue = transformData;

                //如果仍然没匹配到字段名，则进行掩码字段判断
                if (MaskTool.EnableFASMaskStorage(column.MaskingRule))
                {
                    //进行update比较变更集时，生成了update
                    string originDataStr = item.NewValue?.ToString();
                    //获取掩码值
                    string newMaskVal = MaskTool.CalculateMaskData(column.MaskingRule, originDataStr);
                    //获取掩码物理字段名
                    string maskFieldName = DataColumn.GetMaskColumnName(cno.DataObject, column.ColumnName);
                    TChangeSetItem maskFieldChangeItem = new TChangeSetItem
                    {
                        ColumnCode = maskFieldName,
                        ColumnName = column.DisplayName + "掩码",
                        InitValue = null,
                        InitDisplayName = null,
                        OldValue = null,
                        OldDisplayValue = null,
                        NewValue = newMaskVal,
                        NewDisplayValue = null,
                        SortOrder = ele.Sort,
                        DataType = ele.DataType,
                    };
                    maskItems.Add(maskFieldChangeItem);
                }

                //全局校验输入参数是否合规
                ItemCheckResult checkResult = CDOValidateChecker.Check(cno, ele, inputVal, checkFlag, _controlContext);
                CDOValidateCheckResult.Add(checkResult);
            }

            //将不存在的列排除到变更集之外，包括掩码列
            if (removeItems.Count > 0)
            {
                removeItems.ForEach(i => changeSet.ChangeSetItems.Remove(i));
            }

            //将生成的掩码变更项添加到变更集中
            if (maskItems.Count > 0)
            {
                changeSet.ChangeSetItems.AddRange(maskItems);
            }

            //将没有权限变更的列排除到变更集之外
            if (noAuthItems.Count > 0)
            {
                noAuthItems.ForEach(i => changeSet.ChangeSetItems.Remove(i));
            }
        }

        /// <summary>
        /// 特殊处理时间戳类型
        /// </summary>
        /// <param name="inputVal"></param>
        /// <returns></returns>
        private object ConvertTimestampData(object inputVal)
        {
            string timeStr = null;
            try
            {
                if (inputVal != null)
                {
                    if (inputVal is DateTime dtVal)
                    {
                        return dtVal;
                    }
                    else if (inputVal is string strVal)
                    {
                        if (string.IsNullOrEmpty(strVal))
                        {
                            return fasMinDate;
                        }
                        timeStr = strVal;
                    }
                    else if (inputVal is TRecord recordVal)
                    {
                        timeStr = recordVal[nameof(Timestamp.Data)]?.ToString();
                    }
                    else if (inputVal is JToken tokenVal)
                    {
                        if (tokenVal.Type == JTokenType.String)
                        {
                            timeStr = (tokenVal as JValue).Value.ToString();
                        }
                        else if (tokenVal.Type == JTokenType.Object)
                        {
                            JObject obj = (JObject)tokenVal;
                            if (((IDictionary<string, JToken>)obj).ContainsKey(nameof(Timestamp.Data)))
                            {
                                timeStr = obj[nameof(Timestamp.Data)]?.ToString();
                            }
                        }
                    }
                    else
                    {
                        timeStr = inputVal?.ToString();
                    }

                    if (timeStr != null && !string.IsNullOrEmpty(timeStr))
                    {
                        inputVal = System.Convert.ToDateTime(timeStr);   //使用Data属性
                        return inputVal;
                    }
                }
                return fasMinDate;
            }
            catch (Exception e)
            {
                throw new Exception(string.Format(CommonStringClassEngine.DataContextUtil_ConvertTimestampData,inputVal,timeStr,e.Message,e), e);
            }
        }

        private void InitContext(CompositeDataObject cdo)
        {
            _controlContext.Clear();
            _controlContext["CDO"] = cdo;
            _controlContext["DbType"] = EnumTools.Convert<DatabaseType>(cdo.RootCompositeNodeObject.DataObject.Database.DbType);
        }

        private Dictionary<string, object> GetNewContext()
        {
            return GetCopy(_controlContext);
        }

        private Dictionary<string, object> GetCopy(Dictionary<string, object> dict)
        {
            Dictionary<string, object> shallowCopy = new Dictionary<string, object>();
            if (dict != null)
            {
                foreach (var key in dict.Keys)
                {
                    shallowCopy[key] = dict[key];
                }
            }
            return shallowCopy;
        }
        #endregion
    }
}
